//! $Id: IContainer.h 27 2011-09-16 13:28:08Z skagget77 $
//!
//! @file    IContainer.h
//! @author  Johan Andersson <skagget77@gmail.com>
//! @date    Mon Oct 12 08:21 2009
//! @ingroup IoCpp
//!

#if !defined(IOCPP_ICONTAINER)
#define IOCPP_ICONTAINER

#include "Exception.h"
#include "TypeInfo.h"

#include <boost/any.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>

#include <string>
#include <vector>

namespace IoCpp
{
   //!
   //! ProviderNameCollection definition.
   //!
   typedef std::vector<std::string> ProviderNameCollection;

   typedef std::vector<std::wstring> FileCollection;
   typedef std::vector<std::wstring> PathCollection;


   //!
   //! @author  Johan Andersson <skagget77@gmail.com>
   //! @date    Thu Oct 02 07:35 2009
   //! @ingroup IoCppDetail
   //!
   //! The IContainer class.
   //!
   class IContainer
   {
   public :
      /*
      template<class T1>
      void AddImplementation( const std::string& name ) {
         DoAddImplementation(typeid(T1), name);
      }
      */

      //!
      //! Adds a provider to the container.
      //!
      //! @param name The name.
      //!
      //! @param provider The provider.
      //!
      template<class T1>
      void AddProvider( const std::string& name, boost::function<T1 ()> provider ) {
         DoAddProvider(typeid(T1), name, provider);
      }

      //!
      //! Injects an instance of the specified type.
      //!
      //! @return An instance of the specified type.
      //!
      //! @throws TypeNotFoundException When the container cannot find the type
      //!         to inject.
      //!
      //! @throws ProviderNotFoundException When the container cannot find
      //!         a provider for the type to inject.
      //!
      //! @throws ProviderOfWrongTypeException When the provider does not create
      //!         instances that match the type to inject.
      //!
      template<class T1>
      T1 Inject() const
      {
         TypeInfo type = typeid(T1);

         try {
            return boost::any_cast<boost::function<T1 ()> >(DoInject(type))();
         }
         catch(boost::bad_any_cast&) 
         {
            throw ProviderOfWrongTypeException(
               "Provider does not create instances of type " + type.Name()
            );
         }
      }

      //!
      //! Injects an instance of the specified type.
      //!
      //! @param  name The name of the instance to inject.
      //!
      //! @return An instance of the specified type.
      //!
      //! @throws TypeNotFoundException When the container cannot find the type
      //!         to inject.
      //!
      //! @throws ProviderNotFoundException When the container cannot find
      //!         a provider for the type to inject.
      //!
      //! @throws ProviderOfWrongTypeException When the provider does not create
      //!         instances that match the type to inject.
      //!
      template<class T1>
      T1 Inject( const std::string& name ) const
      {
         TypeInfo type = typeid(T1);

         try {
            return boost::any_cast<boost::function<T1 ()> >(DoInject(type, name))();
         }
         catch(boost::bad_any_cast&) 
         {
            throw ProviderOfWrongTypeException(
               "Provider (" + name + ") does not create instances of type " + type.Name()
            );
         }
      }

      //!
      //! Returns the name of all provider to which the specified type is
      //! bound.
      //!
      //! @return Name of all providers to which the specified type is bound.
      //!
      //! @throws TypeNotFoundException When the container cannot find
      //!         the specified type.
      //!
      template<class T1>
      ProviderNameCollection GetAllProviders() const {
         return DoGetAllProviders(typeid(T1));
      }

      /*!
       *
       * \param files
       * \param paths
       * \param thiz
       */
      void Scan( const FileCollection& files, const FileCollection& optionalFiles, const PathCollection& paths, const PathCollection& optionalPaths, bool thiz ) {
         DoScan(files, optionalFiles, paths, optionalPaths, thiz);
      }

      //!
      //! Destructor.
      //!
      virtual ~IContainer() {
      }

   private :
      /*
      virtual void DoAddImplementation( const TypeInfo& type, const std::string& name ) = 0;
      */

      //!
      //! Adds a provider to the container.
      //!
      //! @param name The name.
      //!
      //! @param provider The provider.
      //!
      virtual void DoAddProvider( const TypeInfo& type, const std::string& name, boost::any provider ) = 0;

      //!
      //! Injects an instance of the specified type.
      //!
      //! @param  type The type.
      //!
      //! @return An instance of the specified type.
      //!
      //! @throws TypeNotFoundException When the container cannot find the type
      //!         to inject.
      //!
      //! @throws ProviderNotFoundException When the container cannot find
      //!         a provider for the type to inject.
      //!
      virtual boost::any DoInject( const TypeInfo& type ) const = 0;

      //!
      //! Injects an instance of the specified type.
      //!
      //! @param  type The type.
      //!
      //! @param  name The name of the instance to inject.
      //!
      //! @return An instance of the specified type.
      //!
      //! @throws TypeNotFoundException When the container cannot find the type
      //!         to inject.
      //!
      //! @throws ProviderNotFoundException When the container cannot find
      //!         a provider for the type to inject.
      //!
      virtual boost::any DoInject( const TypeInfo& type, const std::string& name ) const = 0;

      //!
      //! Returns the name of all provider to which the specified type is
      //! bound.
      //!
      //! @param  type The type.
      //!
      //! @return Name of all providers to which the specified type is bound.
      //!
      //! @throws TypeNotFoundException When the container cannot find the
      //!         specified type.
      //!
      virtual ProviderNameCollection DoGetAllProviders( const TypeInfo& type ) const = 0;

      /*!
       *
       * \param files
       * \param paths
       * \param thiz
       */
      virtual void DoScan( const FileCollection& files, const FileCollection& optionalFiles, const PathCollection& paths, const PathCollection& optionalPaths, bool thiz ) = 0;
   };


   //!
   //! IContainerPtr definition.
   //!
   typedef boost::shared_ptr<IoCpp::IContainer> IContainerPtr;
}

#endif   // IOCPP_ICONTAINER
